/*-------------------------------------------------------------------------
 *
 * xid.c
 *      POSTGRES transaction identifier and command identifier datatypes.
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      src/backend/utils/adt/xid.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <limits.h>

#include "access/multixact.h"
#include "access/transam.h"
#include "access/xact.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"

#define PG_GETARG_TRANSACTIONID(n)    DatumGetTransactionId(PG_GETARG_DATUM(n))
#define PG_RETURN_TRANSACTIONID(x)    return TransactionIdGetDatum(x)

#define PG_GETARG_COMMANDID(n)        DatumGetCommandId(PG_GETARG_DATUM(n))
#define PG_RETURN_COMMANDID(x)        return CommandIdGetDatum(x)


Datum
xidin(PG_FUNCTION_ARGS)
{
    char       *str = PG_GETARG_CSTRING(0);

    PG_RETURN_TRANSACTIONID((TransactionId) strtoul(str, NULL, 0));
}

Datum
xidout(PG_FUNCTION_ARGS)
{
    TransactionId transactionId = PG_GETARG_TRANSACTIONID(0);
    char       *result = (char *) palloc(16);

    snprintf(result, 16, "%lu", (unsigned long) transactionId);
    PG_RETURN_CSTRING(result);
}

/*
 *        xidrecv            - converts external binary format to xid
 */
Datum
xidrecv(PG_FUNCTION_ARGS)
{
    StringInfo    buf = (StringInfo) PG_GETARG_POINTER(0);

    PG_RETURN_TRANSACTIONID((TransactionId) pq_getmsgint(buf, sizeof(TransactionId)));
}

/*
 *        xidsend            - converts xid to binary format
 */
Datum
xidsend(PG_FUNCTION_ARGS)
{
    TransactionId arg1 = PG_GETARG_TRANSACTIONID(0);
    StringInfoData buf;

    pq_begintypsend(&buf);
    pq_sendint(&buf, arg1, sizeof(arg1));
    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

/*
 *        xideq            - are two xids equal?
 */
Datum
xideq(PG_FUNCTION_ARGS)
{
    TransactionId xid1 = PG_GETARG_TRANSACTIONID(0);
    TransactionId xid2 = PG_GETARG_TRANSACTIONID(1);

    PG_RETURN_BOOL(TransactionIdEquals(xid1, xid2));
}

/*
 *        xidneq            - are two xids different?
 */
Datum
xidneq(PG_FUNCTION_ARGS)
{
    TransactionId xid1 = PG_GETARG_TRANSACTIONID(0);
    TransactionId xid2 = PG_GETARG_TRANSACTIONID(1);

    PG_RETURN_BOOL(!TransactionIdEquals(xid1, xid2));
}

/*
 *        xid_age            - compute age of an XID (relative to latest stable xid)
 */
Datum
xid_age(PG_FUNCTION_ARGS)
{
    TransactionId xid = PG_GETARG_TRANSACTIONID(0);
    TransactionId now = GetStableLatestTransactionId();

    /* Permanent XIDs are always infinitely old */
    if (!TransactionIdIsNormal(xid))
        PG_RETURN_INT32(INT_MAX);

    PG_RETURN_INT32((int32) (now - xid));
}

/*
 *        mxid_age            - compute age of a multi XID (relative to latest stable mxid)
 */
Datum
mxid_age(PG_FUNCTION_ARGS)
{
    TransactionId xid = PG_GETARG_TRANSACTIONID(0);
    MultiXactId now = ReadNextMultiXactId();

    if (!MultiXactIdIsValid(xid))
        PG_RETURN_INT32(INT_MAX);

    PG_RETURN_INT32((int32) (now - xid));
}

/*
 * xidComparator
 *        qsort comparison function for XIDs
 *
 * We can't use wraparound comparison for XIDs because that does not respect
 * the triangle inequality!  Any old sort order will do.
 */
int
xidComparator(const void *arg1, const void *arg2)
{
    TransactionId xid1 = *(const TransactionId *) arg1;
    TransactionId xid2 = *(const TransactionId *) arg2;

    if (xid1 > xid2)
        return 1;
    if (xid1 < xid2)
        return -1;
    return 0;
}

/*****************************************************************************
 *     COMMAND IDENTIFIER ROUTINES                                             *
 *****************************************************************************/

/*
 *        cidin    - converts CommandId to internal representation.
 */
Datum
cidin(PG_FUNCTION_ARGS)
{
    char       *str = PG_GETARG_CSTRING(0);

    PG_RETURN_COMMANDID((CommandId) strtoul(str, NULL, 0));
}

/*
 *        cidout    - converts a cid to external representation.
 */
Datum
cidout(PG_FUNCTION_ARGS)
{
    CommandId    c = PG_GETARG_COMMANDID(0);
    char       *result = (char *) palloc(16);

    snprintf(result, 16, "%lu", (unsigned long) c);
    PG_RETURN_CSTRING(result);
}

/*
 *        cidrecv            - converts external binary format to cid
 */
Datum
cidrecv(PG_FUNCTION_ARGS)
{
    StringInfo    buf = (StringInfo) PG_GETARG_POINTER(0);

    PG_RETURN_COMMANDID((CommandId) pq_getmsgint(buf, sizeof(CommandId)));
}

/*
 *        cidsend            - converts cid to binary format
 */
Datum
cidsend(PG_FUNCTION_ARGS)
{
    CommandId    arg1 = PG_GETARG_COMMANDID(0);
    StringInfoData buf;

    pq_begintypsend(&buf);
    pq_sendint(&buf, arg1, sizeof(arg1));
    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

Datum
cideq(PG_FUNCTION_ARGS)
{
    CommandId    arg1 = PG_GETARG_COMMANDID(0);
    CommandId    arg2 = PG_GETARG_COMMANDID(1);

    PG_RETURN_BOOL(arg1 == arg2);
}
